import tkinter as tk
from  tkinter  import ttk
import tkinter.filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from imageio import imread, imsave
import matplotlib.pyplot as plt
import numpy as np
import matplotlib
import xlrd
from tkinter import filedialog 
from PIL import Image,ImageFont,ImageDraw, ImageTk
import cv2
import random
import shelve

matplotlib.rcParams['font.sans-serif']=['SimHei']   # 用黑体显示中文
matplotlib.rcParams['axes.unicode_minus']=False     # 正常显示负号

window = tk.Tk()
window.title('数字图像处理')
window.geometry('1230x700')  

global img_png 
global img_00
global img_Y

counter = 0
 
def Open_Img():
    def resize(w, h, w_box, h_box, pil_image):  
        f1 = 1.0*w_box/w 
        f2 = 1.0*h_box/h  
        factor = min([f1, f2])  
        width = int(w*factor)  
        height = int(h*factor)  
        return pil_image.resize((width, height), Image.ANTIALIAS) 
    w_box = 285 
    h_box = 225
    OpenFile = tk.Tk() #创建新窗口
    OpenFile.withdraw()
    b = tk.filedialog.askopenfilename()   
    Img = Image.open(b)
    global img_Y
    img_Y = Img
    w, h = Img.size 
    global img_00
    img_00 = resize(w, h, w_box, h_box, Img)
    global img_png     
    img_png = ImageTk.PhotoImage(img_00)
    Label2 = tk.Label(window,width=w_box ,height=h_box,image=img_png,bg="white").place(x=20,y=120) #画布
    combobox.current(0)

# 1、灰度直方图及均衡化
def normal():
    def ZF():
        src1_for_hist = np.array(src).reshape(1, -1).tolist()
        f_plot1.clear()
        plt.title("灰度直方图",fontsize='16')
        plt.hist(src1_for_hist,bins=255,density=0)  
        canvas_spice3.draw()

    def ZF_jh():
        src2_for_hist = np.array(dest).reshape(1, -1).tolist()
        f_plot1.clear()
        plt.title("灰度直方图(均衡化)",fontsize='16')
        plt.hist(src2_for_hist,bins=255,density=0)
        canvas_spice4.draw()

    src = np.array(img_00.convert("L"))
    def gray():
        f_plot1.clear()   
        plt.title("灰度图",fontsize='16')
        plt.imshow(src, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice1.draw()
    
    gray_times = []
    for i in range(256):
        gray_times.append(np.sum(src == i))
    normalied_gray= gray_times/np.sum(gray_times)
    rk2sk = []
    for rk in range(256):
        sk = 0
        for k in range(rk):
            sk += normalied_gray[k]
        rk2sk.append(256*sk)
        
    width, height = src.shape
    dest = np.zeros_like(src)    
    for i in range(width-1):
        for j in range(height):
            dest[i][j] = rk2sk[src[i][j]] 

    def gray_jh():       
        f_plot1.clear()
        plt.title("灰度图（均衡化）",fontsize='16')
        plt.imshow(dest, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice2.draw()

    tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
    b_gray = tk.Button(window, text='灰度图',command=gray).place(x=40,y=450,width=110,height=30)
    b_gray_jh = tk.Button(window, text='灰度图(均衡化)',command=gray_jh).place(x=180,y=450,width=110,height=30)
    b_ZF = tk.Button(window, text='灰度直方图',command=ZF).place(x=40,y=490,width=110,height=30)
    b_ZF_jh =tk.Button(window, text='灰度直方图(均衡化)',command=ZF_jh).place(x=180,y=490,width=110,height=30)

# 2、灰度变换    
def vo():
    tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
    temp = img_00.convert("L")
    def gray():
        f_plot1.clear()   
        plt.title("灰度图",fontsize='16')
        plt.imshow(temp, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice1.draw()
        
    def gray_():
        img_gray = temp.point(lambda i: 256 - i - 1)
        f_plot1.clear()   
        plt.title("灰度反转",fontsize='16')
        plt.imshow(img_gray, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice2.draw()

    def log():
        img_log = temp.point(lambda  i: 20*255*np.log(1 + i/255))
        f_plot1.clear()   
        plt.title("对数变换",fontsize='16')
        plt.imshow(img_log, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice3.draw()
        
    def Linear():
        im = temp
        imarray = np.array(im)
        height, width = imarray.shape
        for i in range(height):
        	for j in range(width):
        		aft = int(imarray[i, j] + 55)
        		if aft <= 255 and aft >= 0:
        				imarray[i, j] = aft
        		elif aft > 255:
        			imarray[i, j] = 255
        		else:
        			imarray[i, j] = 0 
        img_lin = Image.fromarray(imarray)
        f_plot1.clear()   
        plt.title("线性变换",fontsize='16')
        plt.imshow(img_lin, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice4.draw()
        
    
    b_gray=tk.Button(window, text='灰度图',command=gray).place(x=40,y=450,width=110,height=30)
    b_gray_=tk.Button(window, text='灰度反转',command=gray_).place(x=180,y=450,width=110,height=30)
    b_log=tk.Button(window, text='对数变换',command=log).place(x=40,y=490,width=110,height=30)
    b_liner=tk.Button(window, text='线性变换',command=Linear).place(x=180,y=490,width=110,height=30)

# 3、几何变换
def JiHe():
    tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
    img1 = cv2.cvtColor(np.array(img_00), cv2.COLOR_BGR2RGB)
    height, width = img1.shape[:2]
    def FD():
        fd = float (e_fd.get())
        f_plot1.clear()
        img_fd = cv2.resize(img1, (int(fd*width), int(fd*height)), interpolation=cv2.INTER_AREA)#放大
        plt.title("放大",fontsize='16')
        plt.imshow(img_fd)
        plt.axis()
        plt.xticks(fontsize=14)
        plt.yticks(fontsize=14)
        canvas_spice1.draw() 
        
    def SX():
        sx = float (e_sx.get())
        f_plot1.clear()
        img_sx = cv2.resize(img1, (int(width/sx), int(height/sx)), interpolation=cv2.INTER_AREA)#缩小
        plt.title("缩小",fontsize='16')
        plt.imshow(img_sx)
        plt.axis()
        plt.xticks(fontsize=14)
        plt.yticks(fontsize=14)
        canvas_spice2.draw() 
        
    def XZ():
        xz = float (e_xz.get())
        f_plot1.clear()
        rex = cv2.getRotationMatrix2D((width/2, height/2), xz, 1)#旋转
        img_xz = cv2.warpAffine(img1, rex, (width, height))
        plt.title("旋转",fontsize='16')
        plt.imshow(img_xz)
        plt.axis()
        plt.xticks(fontsize=14)
        plt.yticks(fontsize=14)
        canvas_spice3.draw() 
        
    def WY():
        xx = int (e_wy_x.get())
        yy = int (e_wy_y.get())        
        imgInfo = img1.shape
        height = imgInfo[0]
        width = imgInfo[1]
        mode = imgInfo[2]
        img_wy = np.zeros(imgInfo, np.uint8)
        for i in range( height - yy):
            for j in range( width - xx):
                img_wy[i + yy, j + xx] = img1[i, j]
                
        f_plot1.clear()
        plt.title("位移",fontsize='16')
        plt.imshow(img_wy)
        plt.axis()
        plt.xticks(fontsize=14)
        plt.yticks(fontsize=14)
        canvas_spice4.draw()
        
    b_fd = tk.Label(window,text='放大倍数').place(x=20, y=590,width=60,height=30)
    e_fd = tk.Entry(window)
    e_fd.place(x=80,y=590, width=50,height=30)
    
    b_xz = tk.Label(window,text='旋转角度').place(x=20, y=630,width=60,height=30)
    e_xz = tk.Entry(window)
    e_xz.place(x=80,y=630, width=50,height=30)
    
    b_sx = tk.Label(window,text='缩小倍数').place(x=150, y=590,width=60,height=30)
    e_sx = tk.Entry(window)
    e_sx.place(x=210,y=590, width=50,height=30)  

    b_wy = tk.Label(window,text='位移: x').place(x=150, y=630,width=60,height=30)
    e_wy_x = tk.Entry(window)
    e_wy_x.place(x=210,y=630, width=30,height=30) 

    b_wy_y = tk.Label(window,text='y').place(x=250, y=630,width=20,height=30)
    e_wy_y = tk.Entry(window)
    e_wy_y.place(x=270,y=630, width=30,height=30)
    
    b_gray=tk.Button(window, text='放大',command=FD).place(x=40,y=450,width=110,height=30)
    b_gray_=tk.Button(window, text='缩小',command=SX).place(x=180,y=450,width=110,height=30)
    b_log=tk.Button(window, text='旋转',command=XZ).place(x=40,y=490,width=110,height=30)
    b_liner=tk.Button(window, text='位移',command=WY).place(x=180,y=490,width=110,height=30)
    
# 4、图像噪声
def noise():
    tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
    def gasuss_noise():
        def noise_gasuss():
            mean = float(entry_gasuss.get())
            var = float(entry_gasuss_2.get())
            img = cv2.cvtColor(np.array(img_00),cv2.COLOR_BGR2RGB) 
            image = np.array(img/255, dtype=float)#将原始图像的像素值进行归一化，除以255使得像素值在0-1之间
            noise = np.random.normal(mean, var ** 0.5, image.shape)#创建一个均值为mean，方差为var呈高斯分布的图像矩阵
            out = image + noise#将噪声和原始图像进行相加得到加噪后的图像
            if out.min() < 0:
                low_clip = -1.
            else:
                low_clip = 0.
            out = np.clip(out, low_clip, 1.0)#clip函数将元素的大小限制在了low_clip和1之间了，小于的用low_clip代替，大于1的用1代替
            img_gasuss = np.uint8(out*255)#解除归一化，乘以255将加噪后的图像的像素值恢复
             
            noise = noise*255 
            
            
            f_plot1.clear()   
            plt.title("高斯噪声（污染图）",fontsize='16')
            plt.imshow(img_gasuss, cmap = 'gray', vmin=0, vmax=255)
            plt.axis('off')
            canvas_spice1.draw()
            f_plot1.clear()   
            plt.title("高斯噪声（噪声图）",fontsize='16')
            plt.imshow(noise, cmap = 'gray', vmin=0, vmax=255)
            plt.axis('off')
            canvas_spice2.draw()
            global img_out
            img_out = img_gasuss
            
            
        tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
        tk.Button(window, text='添加噪声', command=noise_gasuss).place(x=220, y=580,width=60, height=30)
        b_gasuss = tk.Label(window,text='均值').place(x=30, y=580,width=80,height=30)
        entry_gasuss = tk.Entry(window)
        entry_gasuss.place(x=120,y=580, width=80,height=30)
        b_gasuss_2 = tk.Label(window,text='方差').place(x=30, y=620,width=80,height=30)
        entry_gasuss_2 = tk.Entry(window)
        entry_gasuss_2.place(x=120,y=620, width=80,height=30) 
        
    def sp_noise(): 
        def noise_sp():
            prob = float (entry_sp.get())
            img_sp = cv2.cvtColor(np.array(img_00),cv2.COLOR_BGR2RGB)
            output = np.zeros(img_sp.shape,np.uint8)
            noise_out = np.zeros(img_sp.shape,np.uint8)
            thres = 1 - prob
            for i in range(img_sp.shape[0]):
                for j in range(img_sp.shape[1]):
                    rdn = random.random()#随机生成0-1之间的数字
                    if rdn < prob:#如果生成的随机数小于噪声比例则将该像素点添加黑点，即椒噪声
                        output[i][j] = 0
                        noise_out[i][j] = 0
                    elif rdn > thres:#如果生成的随机数大于（1-噪声比例）则将该像素点添加白点，即盐噪声
                        output[i][j] = 255
                        noise_out[i][j] = 255
                    else:
                        output[i][j] = img_sp[i][j]#其他情况像素点不变
                        noise_out[i][j] = 100  
            
            f_plot1.clear()   
            plt.title("椒盐噪声（污染图）",fontsize='16')
            plt.imshow(output, cmap = 'gray', vmin=0, vmax=255)
            plt.axis('off')
            canvas_spice1.draw()
            f_plot1.clear()   
            plt.title("椒盐噪声（噪声图）",fontsize='16')
            plt.imshow(noise_out, cmap = 'gray', vmin=0, vmax=255)
            plt.axis('off')
            canvas_spice2.draw()   
            global img_out
            img_out = output
            
            
        tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
        tk.Button(window, text='添加噪声', command=noise_sp).place(x=220, y=580,width=60, height=30)
        b_sp = tk.Label(window,text='噪声比例').place(x=30, y=580,width=80,height=30)
        entry_sp = tk.Entry(window)
        entry_sp.place(x=120,y=580, width=80,height=30)

    def blur(img):#空域（中值滤波）
        img2 = cv2.medianBlur(img, 3)
        return img2
            
    def low_pass(img):#频域（低通滤波）
        img2 = cv2.imread(np.array2string(img),0)
        
        fft = cv2.dft(np.float32(img2), flags=cv2.DFT_COMPLEX_OUTPUT)#傅立叶变换
        dshift = np.fft.fftshift(fft)#将低频移动到中心
        #使用一个矩形窗口对图像进行掩模操作从而去除高频分量
        rows, cols = img2.shape[:2]
        mid_row, mid_col = int(rows / 2), int(cols / 2)     
        mask = np.zeros((rows, cols, 2), np.float32)
        mask[mid_row - 10:mid_row + 10, mid_col - 10:mid_col + 10] = 1
        fft_filtering = dshift * mask
        
        ishift = np.fft.ifftshift(fft_filtering)#傅立叶逆变换
        image_filtering = cv2.idft(ishift)
        image_filtering = cv2.magnitude(image_filtering[:, :, 0], image_filtering[:, :, 1])
        
        image_filtering=np.abs(image_filtering)
        image_filtering=(image_filtering-np.amin(image_filtering))/(np.amax(image_filtering)-np.amin(image_filtering))
        
        #cv2.normalize(image_filtering, image_filtering, 0, 1, cv2.NORM_MINMAX)#归一化
        return image_filtering
        
    def show_low_pass():
        img_low_pass = low_pass(img_out)
        f_plot1.clear()   
        plt.title("低通滤波",fontsize='16')
        plt.imshow(img_low_pass, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        plt.show()
        canvas_spice4.draw()
        
    def show_blur():
        img_blur = blur(img_out)
        f_plot1.clear()   
        plt.title("中值滤波",fontsize='16')
        plt.imshow(img_blur, vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice3.draw()
    
    b_noiso = tk.Button(window, text='椒盐噪声',command=sp_noise).place(x=40,y=450,width=110,height=30)
    b_show = tk.Button(window, text='高斯噪声',command=gasuss_noise).place(x=180,y=450,width=110,height=30)
    b_show1 = tk.Button(window, text='中值滤波',command=show_blur).place(x=40,y=490,width=110,height=30)
    b_show2 = tk.Button(window, text='低通滤波',command=show_low_pass).place(x=180,y=490,width=110,height=30)
    
# 5、边缘提取
def bianyuan():
    tk.Label(window, text="",width=40,height=6).place(x=20, y=570)
    # robert 算子[[-1,-1],[1,1]]
    def robert(img):
    	r, c = img.shape
    	r_sunnzi = [[-1,-1],[1,1]]
    	for x in range(r):
    		for y in range(c):
    			if (y + 2 <= c) and (x + 2 <= r):
    				imgChild = img[x:x+2, y:y+2]
    				list_robert = r_sunnzi*imgChild
    				img[x, y] = abs(list_robert.sum())		# 求和加绝对值
    	return img   				
    # # sobel算子的实现
    def sobel(img):
    	r, c = img.shape
    	new_image = np.zeros((r, c))
    	new_imageX = np.zeros(img.shape)
    	new_imageY = np.zeros(img.shape)
    	s_suanziX = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])		# X方向
    	s_suanziY = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])		
    	for i in range(r-2):
    		for j in range(c-2):
    			new_imageX[i+1, j+1] = abs(np.sum(img[i:i+3, j:j+3] * s_suanziX))
    			new_imageY[i+1, j+1] = abs(np.sum(img[i:i+3, j:j+3] * s_suanziY))
    			new_image[i+1, j+1] = (new_imageX[i+1, j+1]*new_imageX[i+1,j+1] + new_imageY[i+1, j+1]*new_imageY[i+1,j+1])**0.5
    	return np.uint8(new_image)	# 无方向算子处理的图像     
    # Laplace算子
    def laplace(img):
        r, c = img.shape
        new_image = np.zeros((r, c))
        L_sunnzi = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]])
        #L_sunnzi = np.array([[1,1,1],[1,-8,1],[1,1,1]])			
        for i in range(r-2):
        	for j in range(c-2):
        		new_image[i+1, j+1] = abs(np.sum(img[i:i+3, j:j+3] * L_sunnzi))
        return np.uint8(new_image)
    
    img = cv2.cvtColor(np.array(img_00),cv2.COLOR_BGR2GRAY)
    def gray():
        f_plot1.clear()   
        plt.title("灰度图",fontsize='16')
        plt.imshow(img, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice1.draw()
    def robert_out():
        out_robert = robert(img)
        f_plot1.clear()   
        plt.title("robert算子",fontsize='16')
        plt.imshow(out_robert, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice2.draw()
    def sobel_out():
        out_sobel = sobel(img)
        f_plot1.clear()   
        plt.title("sobel算子",fontsize='16')
        plt.imshow(out_sobel, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice3.draw()
    def laplace_out():
        out_laplace = laplace(img)
        f_plot1.clear()   
        plt.title("laplace算子",fontsize='16')
        plt.imshow(out_laplace, cmap = 'gray', vmin=0, vmax=255)
        plt.axis('off')
        canvas_spice4.draw()
    
    b_gray=tk.Button(window, text='灰度图',command=gray).place(x=40,y=450,width=110,height=30)
    b_robert_out=tk.Button(window, text='robert算子',command=robert_out).place(x=180,y=450,width=110,height=30)
    b_sobel_out=tk.Button(window, text='sobel算子',command=sobel_out).place(x=40,y=490,width=110,height=30)
    b_laplace_out=tk.Button(window, text='laplace算子',command=laplace_out).place(x=180,y=490,width=110,height=30)
    
Frame1 = tk.LabelFrame(window,height=80, width=300).place(x=15, y=15)#名字框 
x1 = tk.Label(window, text='学号：1812160107').place(x=60, y=20,width=200,height=20)
x2 = tk.Label(window, text='姓名：鲨鱼掉了啊').place(x=60, y=45,width=200,height=20)

Frame2 = tk.LabelFrame(window,text="原图显示", height=450, width=300).place(x=15, y=100)#预览框
Frame3 = tk.LabelFrame(window, text="结果显示", height=680, width=900).place(x=320, y=5)#显示框
Frame4 = tk.LabelFrame(window, text="参数",height=135, width=300).place(x=15, y=550)#参数框
b = tk.Button(window, text='选择图片', command=Open_Img).place(x=100,y=355,width=110,height=30)#选择图片按钮
Label2 = tk.Label(window,width=40,height=13,bg="white").place(x=20,y=120) #画布    

fig = plt.figure(figsize=(4.5,3.3),dpi=100)#图像比例
f_plot1 =fig.add_subplot(111)#划分区域
canvas_spice1 = FigureCanvasTkAgg(fig, window)
canvas_spice1.get_tk_widget().place(x=330,y=25)#放置位置
canvas_spice2 = FigureCanvasTkAgg(fig, window)
canvas_spice2.get_tk_widget().place(x=760,y=25)#放置位置
canvas_spice3 = FigureCanvasTkAgg(fig, window)
canvas_spice3.get_tk_widget().place(x=330,y=340)#放置位置
canvas_spice4 = FigureCanvasTkAgg(fig, window)
canvas_spice4.get_tk_widget().place(x=760,y=340)#放置位置

values = ["选择图像处理方式","灰度直方图","灰度变换","几何变换","滤波","边缘提取"]
def handler(event):
    current = combobox.current()
    value = values[current]
    func_map = {
        "灰度直方图": normal,
        "灰度变换": vo,
        "滤波":noise,
        "边缘提取": bianyuan,
        "几何变换":JiHe
    }
    func = func_map.get(value, current)
    func()
combobox = ttk.Combobox(window, values=values, state="readonly")
combobox.bind('<<ComboboxSelected>>', handler)
combobox.place(x=30,y=400,relwidth=0.22,height=30)
combobox.current(0)    

window.mainloop()